package com.zzedu.framework.security;

import com.zzedu.framework.filer.HealthCheckFilter;
import com.zzedu.framework.filer.SessionDataSynFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.csrf.CsrfTokenRequestAttributeHandler;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.session.web.http.HeaderHttpSessionIdResolver;
import org.springframework.session.web.http.HttpSessionIdResolver;
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;

/**
 * @author Administrator
 * @EnableWebSecurity 拦截所有请求
 * @EnableGlobalMethodSecurity(prePostEnabled = true) 开启细粒度控制
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SpringSecuityConfig {
    @Autowired
    IgnoreUrlsConfig ignoreUrlsConfig;

    /**
     * 是否啟用csrf
     */
    @Value("${secure.csrfEnable}")
    private boolean csrfEnable;

    public boolean getCsrfEnable() {
        return csrfEnable;
    }

    public void setCsrfEnable(boolean csrfEnable) {
        this.csrfEnable = csrfEnable;
    }

    /**
     * 获取AuthenticationManager（认证管理器），登录时认证使用
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence rawPassword) {
                return String.valueOf(rawPassword);
            }

            @Override
            public boolean matches(CharSequence rawPassword, String encodedPassword) {
                //其他地方验证,始终返回true
                return true;
            }
        };
    }

    /**
     * 允许抛出用户不存在的异常
     *
     * @param myUserDetailsService myUserDetailsService
     * @return DaoAuthenticationProvider
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider(MyUserDetailsServiceImpl myUserDetailsService, PasswordEncoder passwordEncoder) {
        final DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(myUserDetailsService);
        provider.setHideUserNotFoundExceptions(false);
        provider.setPasswordEncoder(passwordEncoder);
        return provider;
    }

    @Bean("httpSessionIdResolver")
    public HttpSessionIdResolver httpSessionIdResolver() {
        return new HeaderHttpSessionIdResolver("Authentication");
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http,
                                                   MyLoginFilter loginFilter,
                                                   MyAuthenticationHandler authenticationHandler,
                                                   SessionDataSynFilter sessionDataSynFilter,
                                                   HealthCheckFilter healthCheckFilter,
                                                   HandlerMappingIntrospector introspector
    ) throws Exception {
        MvcRequestMatcher.Builder mvc = new MvcRequestMatcher.Builder(introspector);
        int len = ignoreUrlsConfig.getUrls().size();
        int i = 0;
        RequestMatcher[] requestMatchers = new RequestMatcher[len];
        for (String url : ignoreUrlsConfig.getUrls()) {
            requestMatchers[i++] = mvc.pattern(url);
        }

        RequestMatcher druidMvcRequestMatcher = new AntPathRequestMatcher("/druid/*");
        //路径配置
        http.authorizeHttpRequests()
                .requestMatchers(druidMvcRequestMatcher).authenticated()
                .requestMatchers(requestMatchers).permitAll()
                .anyRequest().authenticated()
        ;
// [org.springframework.security.web.session.DisableEncodeUrlFilter@2db75e32,
// org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@1f2d0ca2,
// org.springframework.security.web.context.SecurityContextHolderFilter@642b71d6,
// org.springframework.security.web.header.HeaderWriterFilter@1390a43a,
// org.springframework.security.web.csrf.CsrfFilter@7925e772,
// org.springframework.security.web.authentication.logout.LogoutFilter@74130456,
// com.zzelt.framework.security.MyLoginFilter@2c7375da,
// org.springframework.security.web.session.ConcurrentSessionFilter@3c7787a7,
// org.springframework.security.web.savedrequest.RequestCacheAwareFilter@258291de,
// org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5625e7e1,
// org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter@3dfaa1e4,
// org.springframework.security.web.authentication.AnonymousAuthenticationFilter@e353e1d,
// org.springframework.security.web.session.SessionManagementFilter@23169374,
// org.springframework.security.web.access.ExceptionTranslationFilter@d8a2b1b,
// org.springframework.security.web.access.intercept.AuthorizationFilter@5263f554]
        //登陆
        http.addFilterBefore(healthCheckFilter,org.springframework.security.web.session.DisableEncodeUrlFilter.class);
        http.addFilterAt(loginFilter, UsernamePasswordAuthenticationFilter.class);
        http.addFilterAfter(sessionDataSynFilter, org.springframework.security.web.access.intercept.AuthorizationFilter.class);
        //配置自定义登陆流程后需要关闭 否则可以使用原有登陆方式

        //登出
        http.logout().logoutUrl("/logout").logoutSuccessHandler(authenticationHandler);
        if (getCsrfEnable()) {
            //csrf验证 存储到Cookie中
            http.csrf(httpSecurityCsrfConfigurer -> {
                httpSecurityCsrfConfigurer.csrfTokenRepository(new HttpSessionCsrfTokenRepository())
                        .csrfTokenRequestHandler(new CsrfTokenRequestAttributeHandler())
                        .ignoringRequestMatchers(requestMatchers)
                        .ignoringRequestMatchers(druidMvcRequestMatcher)
                        .ignoringRequestMatchers(mvc.pattern("/websocket/**"));
            });
        } else {
            http.csrf(httpSecurityCsrfConfigurer -> {
                httpSecurityCsrfConfigurer.disable();
            });
        }
        //会话管理
        http.sessionManagement(httpSecuritySessionManagementConfigurer -> {
            httpSecuritySessionManagementConfigurer.maximumSessions(-1)
                    .expiredSessionStrategy(authenticationHandler);
        });

//        http.sessionManagement()
//                .maximumSessions(-1)
//                .expiredSessionStrategy(authenticationHandler)
        //引入redis-session依赖后已不再需要手动配置 sessionRegistry
//                .sessionRegistry(new SpringSessionBackedSessionRegistry<>(new RedisIndexedSessionRepository(RedisConfig.createRedisTemplate())))
        //禁止后登陆挤下线
//               .maxSessionsPreventsLogin(true)
        ;
//        http.headers().xssProtection();
//        http.headers().frameOptions().disable(); //用于加载页面iframe部分
        // 权限不足时的处理
        http.exceptionHandling(httpSecurityExceptionHandlingConfigurer -> {
            httpSecurityExceptionHandlingConfigurer
                    .accessDeniedHandler(authenticationHandler)
                    .authenticationEntryPoint(authenticationHandler);
        })
        ;
        http.headers(header -> header.frameOptions(frameOptionsConfig -> frameOptionsConfig.sameOrigin()).xssProtection());
        return http.build();
    }
}
